package server

import (
	"fmt"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"shop-api/consts"
	"shop-api/databases/mysql"
	"shop-api/internal/model"
	"sync"

	"github.com/shopspring/decimal"
)

type CreateOrderReq struct {
	AddressID uint `json:"address_id"`
	Coupon    uint `json:"coupon"`
	Goods     []struct {
		SkuID uint `json:"sku_id"`
		Num   uint `json:"num"`
	} `json:"goods"`
	PayType uint   `json:"pay_type"`
	Node    string `json:"node"`
}

func getAmount(in *CreateOrderReq, skuMap map[uint]*model.GoodSKU) (decimal.Decimal, error) {
	amount := decimal.NewFromInt(0)
	for _, val := range in.Goods {
		skuInfo := skuMap[val.SkuID]

		price, err := decimal.NewFromString(skuInfo.Price)
		if err != nil {
			return decimal.Decimal{}, err
		}

		num := decimal.NewFromInt(int64(val.Num))
		amount = amount.Add(price.Mul(num))
	}
	return amount, nil
}

func getDiscountAmount(amount decimal.Decimal, couponInfo *model.Coupon) (decimal.Decimal, error) {
	ninAmount, err := decimal.NewFromString(couponInfo.MinAmount)
	if err != nil {
		return decimal.Decimal{}, err
	}
	if amount.LessThan(ninAmount) {
		return decimal.NewFromInt(0), err
	}

	discountValue, err := decimal.NewFromString(couponInfo.Discount)
	if err != nil {
		return decimal.Decimal{}, err
	}

	switch couponInfo.DiscountType {
	case consts.CouponDiscountTypeFixed:
		return discountValue, nil
	case consts.CouponStateExpired:
		return amount.Sub(amount.Mul(discountValue)), nil
	default:
		return decimal.NewFromInt(0), nil
	}
}

func CreateOrder(userID uint, in *CreateOrderReq) (string, error) {
	wg := sync.WaitGroup{}

	var skuMap map[uint]*model.GoodSKU
	var goodMap map[uint]*model.Good
	var addressInfo *model.UserAddress
	var couponInfo *model.Coupon
	var err error

	wg.Add(3)
	go func() {
		//获取商品信息，包括SKU信息
		skuMap, goodMap, err = getGoodInfos(in)
		wg.Done()
	}()

	go func() {
		//获取地址信息
		addressInfo, err = getAddress(userID, in.AddressID)
		wg.Done()
	}()

	go func() {
		//获取优惠券信息
		couponInfo, err = getCoupon(userID, in.Coupon)
		wg.Done()
	}()

	wg.Wait()
	//计算总金额
	amount, err := getAmount(in, skuMap)
	if err != nil {
		return "", err
	}

	//计算优惠金额
	discountAmount, err := getDiscountAmount(amount, couponInfo)
	if err != nil {
		return "", err
	}

	//生成订单编号
	no := uuid.NewString()

	//创建订单数据及修改库存，事务处理
	err = createOrder(userID, no, addressInfo, in, discountAmount, amount, skuMap, goodMap)
	if err != nil {
		return "", err
	}

	//获取支付链接
	return "", nil
}

func createOrder(
	userID uint,
	no string,
	addressInfo *model.UserAddress,
	in *CreateOrderReq,
	discountAmount decimal.Decimal,
	amount decimal.Decimal,
	skuMap map[uint]*model.GoodSKU,
	goodMap map[uint]*model.Good,
) error {
	return mysql.WithClient(func(db *gorm.DB) error {
		return db.Transaction(func(tx *gorm.DB) error {
			orderMod := model.Order{
				UserID:        userID,
				No:            no,
				Name:          addressInfo.Name,
				Phone:         addressInfo.Phone,
				Address:       addressInfo.Address,
				Note:          in.Node,
				CouponID:      in.Coupon,
				Favorable:     discountAmount.StringFixed(2),
				Amount:        amount.StringFixed(2),
				PayType:       in.PayType,
				CommentStatus: false,
			}

			var up []*model.UpdateStock
			var orderGodos []*model.OrderGoods
			for _, val := range in.Goods {
				skuInfo := skuMap[val.SkuID]
				goodInfo := goodMap[skuInfo.GoodID]
				orderGodos = append(orderGodos, &model.OrderGoods{
					GoodID:   goodInfo.ID,
					Title:    goodInfo.Name,
					Img:      goodInfo.Image,
					SKUId:    skuInfo.ID,
					Quantity: val.Num,
					Price:    skuInfo.Price,
				})

				up = append(up, &model.UpdateStock{
					SkuID: val.SkuID,
					Num:   val.Num,
				})
			}

			err := orderMod.CreateOrderWithTx(tx, orderGodos)
			if err != nil {
				return err
			}

			skuMod := new(model.GoodSKU)
			return skuMod.UpdateStockWithTx(tx, up)
		})
	})
}

func getCoupon(userID, id uint) (*model.Coupon, error) {
	userCouponMod := new(model.UserCoupon)
	userCouponInfo, err := userCouponMod.GetCoupon(id)
	if err != nil {
		return nil, err
	}
	if userCouponInfo.UserID != userID {
		return nil, fmt.Errorf("优惠券权限异常")
	}
	switch userCouponInfo.Status {
	case consts.CouponStateUsed:
		return nil, fmt.Errorf("优惠券已使用")
	case consts.CouponStateExpired:
		return nil, fmt.Errorf("优惠券已过期")
	}

	couponMod := new(model.Coupon)
	return couponMod.GetCoupon(id)
}

func getAddress(userID, id uint) (*model.UserAddress, error) {
	addMod := new(model.UserAddress)
	addressInfo, err := addMod.GetAddrByAddrId(id)
	if err != nil {
		return nil, err
	}

	if addressInfo.UserID != userID {
		return nil, fmt.Errorf("地址权限异常")
	}
	return addressInfo, nil
}

func getGoodInfos(in *CreateOrderReq) (map[uint]*model.GoodSKU, map[uint]*model.Good, error) {
	var skuIDs []uint
	for _, val := range in.Goods {
		skuIDs = append(skuIDs, val.SkuID)
	}

	skuMod := new(model.GoodSKU)
	skuInfos, err := skuMod.GetBySkuIDs(skuIDs)
	if err != nil {
		return nil, nil, err
	}
	if len(skuIDs) != len(skuInfos) {
		return nil, nil, fmt.Errorf("商品不存在")
	}

	var goodIDs []uint
	skuMap := make(map[uint]*model.GoodSKU)
	for _, val := range skuInfos {
		skuMap[val.ID] = val
		goodIDs = append(goodIDs, val.GoodID)
	}

	goodMod := new(model.Good)
	goodInfos, err := goodMod.GetListByIDs(goodIDs)
	if err != nil {
		return nil, nil, err
	}

	goodMap := make(map[uint]*model.Good)
	for _, val := range goodInfos {
		goodMap[val.ID] = val
	}

	return skuMap, goodMap, nil
}
